/***************************************************************************************************
 *Copyright(C),2010-2017,Sumscope
 *FileName	:  pinyin_search_engine.h
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/06/04
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#pragma once
#include <core/container/CHashMap.h>
#include <core/instance/singleton.h>
#include <core/strings/WideString.h>
#include <core/strings/string_convert.h>
#include <core/strings/string_util.h>
#include <core/sync/lock.h>
#include <core/time/time.h>

#include "../pinyin_search.h"
#include "pinyin_search_define.h"
#include "pinyin_search_idfile.h"
#include "pinyin_search_idstore.h"
#include "pinyin_search_idvec.h"
#include "pinyin_search_key.h"
#include "pinyin_search_set.h"

namespace qb {
namespace base {
class StringPage;
namespace internal {
class BondChangeBuffer;
class IndexListMap {
 public:
  typedef qb::base::CHashMap<InverseKey, IndexIdRange*> InverseSetPtrHashMap;
  typedef InverseSetPtrHashMap::iterator iterator;
  typedef map<InverseKey, IndexIdRange*> InverseSetPtrMap;

 protected:
  InverseSetPtrHashMap m_hashmap;  // 哈希表
  InverseSetPtrMap m_map;          // 作对比测试用
 public:
  IndexListMap(int capcity);
  ~IndexListMap();
  IndexIdRange* Find(const InverseKey& key);
  void Insert(const InverseKey& key, InverseSet* isets);
  iterator begin() { return m_hashmap.begin(); }
  iterator end() { return m_hashmap.end(); }
  int size() const { return m_hashmap.Count(); }
  int capcity() const { return m_hashmap.Capcity(); }
  int bytes() const;
};
class SpellSearchImpl : public ISpellSearch,
                        public qb::base::Singleton<SpellSearchImpl> {
  typedef qb::base::SpellSearch::Listener Listener;

 public:
  enum kPredictMode {  // 每个索引键的预估尺寸的模式
    kPredictModeNone,  // 不适用预估尺寸
    kPredictModeArray,  // 使用全部数组的模式,对应于KeyStat的MagicKeyCapcity
    kPredictModeSlot,  // 使用插槽模式,对应于KeyStat的Magic
  };

 protected:
  typedef qb::base::strinfo strinfo;
  typedef qb::base::StringUtil StringUtil;
  typedef qb::base::HighResTime HighResTime;
  typedef qb::base::BaseString BaseString;
  typedef InverseSet IndexSet;
  typedef std::map<InverseKey, int> PredictMap;
  class InverseKeySet : public std::set<InverseKey> {
    int m_start_index;

   public:
    InverseKeySet() : m_start_index(0) {}
    void SetStart(int start) {
      start = start <= 0 ? 0 : start;
      m_start_index = start;
    }
    int GetStart() const { return m_start_index; }
  };
  class KeyCache {
    typedef CHashMap<wchar_t, InverseKeySet*> CharInverseKeyMap;
    CharInverseKeyMap m_cache;
    InverseKeySet m_temp;

   public:
    KeyCache() {}
    ~KeyCache();
    void AddKey(const InverseKey& key);
    const InverseKeySet* GetKeys(const wstring& text);
    InverseKeySet* GetKeySet(wchar_t code);
    void Dump();
  };
  IndexListMap m_indexes;       // key到IndexIdRange的映射关系
  IndexListMap m_indexes2;      // 只用于有2个字符的key;
  InverseSet m_firstset;        // 第一个键值搜索的结果集
  InverseSet m_tempset;         // 第二个以及后续键值搜索的结果集
  IndexIdStore m_firststore;    // 存储当前搜索的IndexId序列
  IndexIdStore m_currentstore;  //
  KeyCache m_keycache;
  PredictMap m_predictmap;  // 用于预估每个InverseKey可能对应多少个值
  int m_predict_min;  // 当前的尺寸预估的最小值(初始默认值)
  kPredictMode m_predict_mode;  // 当前的尺寸预估模式
  wstring m_lastkey;
  qb::base::CSLock m_lock;  //
  int m_max_index;
  int m_max_count2;
  int m_max_count;  //
  int m_keylen;     //
  unsigned int m_last_tick;
  qb::base::WideString m_comma;  // 用于分隔pinyin.cpp产生的拼音串中间的逗号
  bool m_initialized;  //
  bool
      m_deleteset;  // 用于控制BuildIndex之后,是否删除IndexIdRange中的内存集合m_memory_set,默认true
  bool
      m_flag_check;  // 用于控制是否开启内部测试,通过配置变量控制,默认false,开启后会影响性能
  bool m_flag_usingset;  // 默认为false,表示使用映射文件,否则将使用内存set
  bool m_flag_searchtag;
  bool m_flag_verboscheck;
  bool m_flag_keycache;
  bool m_flag_endcode;
  qb::base::CConvertFast m_convert;  // 用于转码
  qb::base::CConvertFast m_convert_find;
  IndexIdFile m_file;  // 映射文件
 public:
  SpellSearchImpl(int capcity = 60000, int nPageCount = 32,
                  bool verbos = false);
  ~SpellSearchImpl();
  void Dump();

  bool IsReady() const { return m_initialized; }
  void SetReady() { m_initialized = true; }
  void SetFlag(const char* name, bool flag);
  bool GetFlag(const char* name) const;
  virtual void BuildIndex(bool diskless) override;
  virtual void FindBonds(const std::wstring& text, Listener* listener);
  virtual bool AddBondChange(BondChangeBuffer* change);
  virtual void GetAllKeys(std::vector<std::wstring>& keys);

 protected:
  InverseSet* FindInverseSet(const InverseKey& key, InverseSet* target);
  IndexIdStore* UpdateFirstStore(const InverseKey& key);
  IndexIdStore* UpdateFirstStoreWithDoubleKey(const InverseKey& key);
  IndexIdStore* UpdateFirstStoreByCheckLastKey(const std::wstring& key,
                                               int& pos);
  IndexIdStore* FindIndexBySpellMatch(const std::wstring& text);
  void FindBondIndexes(const std::wstring& text, IndexIdVec& first, bool log);
  void FindBondsWithLessKey(const std::wstring& text, Listener* listener);
  void RemoveIndexForSentence(int index, const wchar_t* sentence, int len,
                              bool split);
  void RemoveIndexForKey(int index, const InverseKey& key);
  void AddIndexForSentence(int index, const wchar_t* sentence, int len,
                           bool split);
  void AddIndexForKey(int index, const InverseKey& key);
  bool IntersectWithKey(IndexIdVec& first, const InverseKey& rkey, int step,
                        bool log, int& ncount);

 protected:  // 针对只有2个字符的情况,只限定于kChangeTypeName, kChangeTypeNameEn
             // 和 kChangeTypeCode
  void AddIndexForDoubleKey(int index, const InverseKey& key);
  void RemoveIndexForDoubleKey(int index, const InverseKey& key);

 protected:  // 内部功能测试接口,必须使用宏BASSERT_SPELL来关闭和开启
  bool UseMappingSet() const;
  void CheckAll();
  bool CheckIndexForKey(int index, const InverseKey& key);
  int PredictCount(const InverseKey& key);
};

}  // namespace internal
}  // namespace base
}  // namespace qb
